using DesktopWidgetApp.Server.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace DesktopWidgetApp.Server.Services
{
    /// <summary>
    /// 用户服务实现
    /// </summary>
    public class UserService : IUserService
    {
        private readonly AppDbContext _dbContext;
        private readonly string _jwtSecretKey;
        private readonly string _jwtIssuer;
        private readonly string _jwtAudience;
        private readonly int _jwtExpirationMinutes;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dbContext">数据库上下文</param>
        /// <param name="jwtSecretKey">JWT密钥</param>
        /// <param name="jwtIssuer">JWT颁发者</param>
        /// <param name="jwtAudience">JWT受众</param>
        /// <param name="jwtExpirationMinutes">JWT过期分钟数</param>
        public UserService(AppDbContext dbContext, string jwtSecretKey, string jwtIssuer, string jwtAudience, int jwtExpirationMinutes)
        {
            _dbContext = dbContext;
            _jwtSecretKey = jwtSecretKey;
            _jwtIssuer = jwtIssuer;
            _jwtAudience = jwtAudience;
            _jwtExpirationMinutes = jwtExpirationMinutes;
        }

        /// <summary>
        /// 注册新用户
        /// </summary>
        /// <param name="username">用户名</param>
        /// <param name="email">邮箱</param>
        /// <param name="password">密码</param>
        /// <returns>注册结果</returns>
        public async Task<(bool success, string message, User user)> RegisterUserAsync(string username, string email, string password)
        {
            // 检查用户名是否已存在
            if (await _dbContext.Users.AnyAsync(u => u.Username == username))
            {
                return (false, "用户名已存在", null!);
            }

            // 检查邮箱是否已存在
            if (await _dbContext.Users.AnyAsync(u => u.Email == email))
            {
                return (false, "邮箱已被注册", null!);
            }

            // 验证密码复杂度
            string passwordValidationResult = ValidatePasswordComplexity(password);
            if (!string.IsNullOrEmpty(passwordValidationResult))
            {
                return (false, passwordValidationResult, null!);
            }

            // 生成密码盐和哈希
            string salt = GenerateSalt();
            string passwordHash = HashPassword(password, salt);

            // 创建新用户
            var user = new User
            {
                Id = Guid.NewGuid(),
                Username = username,
                Email = email,
                PasswordHash = passwordHash,
                PasswordSalt = salt,
                RegisteredAt = DateTime.Now,
                Role = Role.User.ToString()
            };

            // 添加到数据库
            _dbContext.Users.Add(user);
            await _dbContext.SaveChangesAsync();

            return (true, "注册成功", user);
        }

        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="username">用户名</param>
        /// <param name="password">密码</param>
        /// <returns>登录结果</returns>
        public async Task<(bool success, string message, string token)> LoginAsync(string username, string password)
        {
            // 查找用户
            var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.Username == username);

            // 验证用户是否存在
            if (user == null)
            {
                return (false, "用户名或密码错误", null!);
            }

            // 检查账户是否被锁定
            if (user.IsLocked && user.LockoutEnd.HasValue && user.LockoutEnd.Value > DateTime.Now)
            {
                var remainingTime = user.LockoutEnd.Value - DateTime.Now;
                return (false, $"账户已被锁定，请在{remainingTime.Minutes}分{remainingTime.Seconds}秒后重试", null!);
            }

            // 如果锁定时间已过，重置锁定状态
            if (user.IsLocked && user.LockoutEnd <= DateTime.Now)
            {
                user.IsLocked = false;
                user.AccessFailedCount = 0;
                user.LockoutEnd = null;
                await _dbContext.SaveChangesAsync();
            }

            // 验证密码
            if (!VerifyPassword(password, user.PasswordSalt, user.PasswordHash))
            {
                // 增加登录失败次数
                user.AccessFailedCount++;
                
                // 如果失败次数达到阈值，锁定账户
                if (user.AccessFailedCount >= 5)
                {
                    user.IsLocked = true;
                    user.LockoutEnd = DateTime.Now.AddMinutes(15); // 锁定15分钟
                    await _dbContext.SaveChangesAsync();
                    return (false, "登录失败次数过多，账户已被锁定15分钟", null!);
                }
                
                await _dbContext.SaveChangesAsync();
                return (false, "用户名或密码错误", null!);
            }

            // 验证用户是否激活
            if (!user.IsActive)
            {
                return (false, "用户账号已被禁用", null!);
            }

            // 登录成功，重置失败次数并更新最后登录时间
            user.LastLoginAt = DateTime.Now;
            user.AccessFailedCount = 0;
            
            // 生成并保存刷新令牌
            string refreshToken = GenerateRefreshToken();
            user.RefreshToken = refreshToken;
            user.RefreshTokenExpiryTime = DateTime.Now.AddDays(7); // 刷新令牌有效期7天
            
            await _dbContext.SaveChangesAsync();

            // 生成JWT令牌
            string token = GenerateJwtToken(user);

            return (true, "登录成功", token);
        }

        /// <summary>
        /// 根据ID获取用户
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <returns>用户信息</returns>
        public async Task<User?> GetUserByIdAsync(Guid userId)
        {
            return await _dbContext.Users.FindAsync(userId);
        }

        /// <summary>
        /// 根据用户名获取用户
        /// </summary>
        /// <param name="username">用户名</param>
        /// <returns>用户信息</returns>
        public async Task<User?> GetUserByUsernameAsync(string username)
        {
            return await _dbContext.Users.SingleOrDefaultAsync(u => u.Username == username);
        }

        /// <summary>
        /// 更新用户信息
        /// </summary>
        /// <param name="user">用户信息</param>
        /// <returns>更新结果</returns>
        public async Task<(bool success, string message)> UpdateUserAsync(User user)
        {
            try
            {
                _dbContext.Users.Update(user);
                await _dbContext.SaveChangesAsync();
                return (true, "更新成功");
            }
            catch (Exception ex)
            {
                return (false, "更新失败：" + ex.Message);
            }
        }

        /// <summary>
        /// 验证JWT令牌
        /// </summary>
        /// <param name="token">JWT令牌</param>
        /// <returns>验证结果</returns>
        public (bool isValid, string userId) ValidateToken(string token)
        {
            try
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(_jwtSecretKey);

                tokenHandler.ValidateToken(token, new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = true,
                    ValidIssuer = _jwtIssuer,
                    ValidateAudience = true,
                    ValidAudience = _jwtAudience,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                }, out SecurityToken validatedToken);

                var jwtToken = (JwtSecurityToken)validatedToken;
                var userId = jwtToken.Claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value;

                return (true, userId);
            }
            catch
            {
                return (false, string.Empty);
            }
        }

        /// <summary>
        /// 生成密码盐
        /// </summary>
        /// <returns>密码盐</returns>
        private string GenerateSalt()
        {
            byte[] saltBytes = new byte[32];
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(saltBytes);
            }
            return Convert.ToBase64String(saltBytes);
        }

        /// <summary>
        /// 哈希密码
        /// </summary>
        /// <param name="password">原始密码</param>
        /// <param name="salt">密码盐</param>
        /// <returns>密码哈希</returns>
        private string HashPassword(string password, string salt)
        {
            using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(salt)))
            {
                byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
                byte[] hashBytes = hmac.ComputeHash(passwordBytes);
                return Convert.ToBase64String(hashBytes);
            }
        }

        /// <summary>
        /// 验证密码
        /// </summary>
        /// <param name="password">原始密码</param>
        /// <param name="salt">密码盐</param>
        /// <param name="storedHash">存储的密码哈希</param>
        /// <returns>验证结果</returns>
        private bool VerifyPassword(string password, string salt, string storedHash)
        {
            string hashToCompare = HashPassword(password, salt);
            return hashToCompare == storedHash;
        }

        /// <summary>
        /// 验证密码复杂度
        /// </summary>
        /// <param name="password">密码</param>
        /// <returns>验证结果，如果为空则表示验证通过</returns>
        private string ValidatePasswordComplexity(string password)
        {
            // 密码长度检查
            if (password.Length < 8)
            {
                return "密码长度至少为8个字符";
            }

            // 包含至少一个大写字母
            if (!password.Any(char.IsUpper))
            {
                return "密码必须包含至少一个大写字母";
            }

            // 包含至少一个小写字母
            if (!password.Any(char.IsLower))
            {
                return "密码必须包含至少一个小写字母";
            }

            // 包含至少一个数字
            if (!password.Any(char.IsDigit))
            {
                return "密码必须包含至少一个数字";
            }

            // 包含至少一个特殊字符
            if (!password.Any(c => "!@#$%^&*(),.?\":{}|<>"
            .Contains(c)))
            {
                return "密码必须包含至少一个特殊字符";
            }

            return string.Empty; // 验证通过
        }

        /// <summary>
        /// 生成JWT令牌
        /// </summary>
        /// <param name="user">用户信息</param>
        /// <returns>JWT令牌</returns>
        private string GenerateJwtToken(User user)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_jwtSecretKey);

            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new[] {
                    new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                    new Claim(ClaimTypes.Name, user.Username),
                    new Claim(ClaimTypes.Email, user.Email),
                    new Claim(ClaimTypes.Role, user.Role)
                }),
                Expires = DateTime.UtcNow.AddMinutes(_jwtExpirationMinutes),
                Issuer = _jwtIssuer,
                Audience = _jwtAudience,
                SigningCredentials = new SigningCredentials(
                    new SymmetricSecurityKey(key),
                    SecurityAlgorithms.HmacSha256Signature
                )
            };

            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }
        
        /// <summary>
        /// 生成刷新令牌
        /// </summary>
        /// <returns>刷新令牌</returns>
        private string GenerateRefreshToken()
        {
            var randomNumber = new byte[64];
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(randomNumber);
            }
            return Convert.ToBase64String(randomNumber);
        }
        
        /// <summary>
        /// 刷新JWT令牌
        /// </summary>
        /// <param name="token">当前JWT令牌</param>
        /// <param name="refreshToken">刷新令牌</param>
        /// <returns>新的JWT令牌</returns>
        public async Task<(bool success, string message, string newToken)> RefreshTokenAsync(string token, string refreshToken)
        {
            // 验证当前令牌
            var (isValid, userIdStr) = ValidateToken(token);
            if (!isValid)
            {
                return (false, "无效的访问令牌", null!);
            }
            
            // 验证用户ID
            if (!Guid.TryParse(userIdStr, out var userId))
            {
                return (false, "无效的用户ID", null!);
            }
            
            // 查找用户
            var user = await _dbContext.Users.FindAsync(userId);
            if (user == null || user.RefreshToken != refreshToken || user.RefreshTokenExpiryTime <= DateTime.Now)
            {
                return (false, "无效的刷新令牌", null!);
            }
            
            // 验证用户是否激活
            if (!user.IsActive)
            {
                return (false, "用户账号已被禁用", null!);
            }
            
            // 生成新的JWT令牌
            string newJwtToken = GenerateJwtToken(user);
            
            // 生成新的刷新令牌（可选：也可以保持原刷新令牌不变）
            string newRefreshToken = GenerateRefreshToken();
            user.RefreshToken = newRefreshToken;
            user.RefreshTokenExpiryTime = DateTime.Now.AddDays(7);
            
            await _dbContext.SaveChangesAsync();
            
            return (true, "令牌刷新成功", newJwtToken);
        }
    }
}